package com.igeek.boot.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.igeek.boot.entity.Items;
import com.igeek.boot.mapper.ItemsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

/**
 * @Author fengqq
 * @Description TODO
 * @Date 2024/9/9  9:48
 */
//@CacheConfig统一设置cacheNames，指定缓存组件的名字。统一设置keyGenerator，指定key的生成器
@CacheConfig(cacheNames = "items"/*,keyGenerator = "keyGenerator"*/)
@Service
@Slf4j
public class ItemsServiceImpl extends ServiceImpl<ItemsMapper, Items> implements ItemsService {

    /**
     * 注解 @Cacheable
     * 一、运行流程：
     * 标注的方法执行之前先来检查缓存中有没有这个数据，默认按照参数的值作为key去查询缓存，
     * 如果没有就运行方法并将结果放入缓存；以后再来调用就可以直接使用缓存中的数据。
     *
     * 二、属性：
     * 1.cacheNames/value属性：指定缓存组件的名字；将方法的返回结果放在哪个缓存中，是列表即可以指定多个缓存；
     * 2.key属性：缓存数据使用的key。默认是使用方法参数的值。
     *      2.1 默认为id传入的值，例如：1
     *      2.2 使用SpEL表达式，
     *          例如：1           即  #id
     *          例如：findOne[1]  即 #root.methodName+'['+#id+']'
     * 3.keyGenerator属性：key的生成器，可以指定key生成器的组件beanName。
     *      3.1 key/keyGenerator：二选一使用
     * 4.cacheManager：指定缓存管理器；或者cacheResolver指定获取解析器
     * 5.condition：指定符合条件的情况下才缓存；
     *      5.1 condition = "#id>0"：id参数的值>0的时候才进行缓存
     *      5.2 condition = "#a0>1"：第一个参数的值>1的时候才进行缓存
     * 6.unless:否定缓存；当unless指定的条件为true，方法的返回值就不会被缓存；可以获取到结果进行判断
     *      6.1 unless = "#result == null"：返回结果为null，结果不缓存；
     *      6.2 unless = "#a0==2":如果第一个参数的值是2，结果不缓存；
     * 7.sync：是否使用异步模式
     */
//    @Cacheable(cacheNames = "items",key = "#id")
//    @Cacheable(cacheNames = "items",key = "#root.methodName+'['+#id+']'")
//   @Cacheable(cacheNames = "items",keyGenerator = "keyGenerator",condition = "#id==1")
    @Cacheable(key = "#id")
    @Override
    public Items findOne(Integer id) {
        return baseMapper.selectById(id);
    }

    /*

     * 注解 @CachePut
     * 既调用方法，又更新缓存数据；修改了数据库的某个数据，同时将方法返回值更新至缓存；
     * 一、运行流程：
     *  1、先调用目标方法
     *  2、将目标方法的结果缓存起来
     * 二、属性：
     *  1.cacheNames/value属性：指定缓存组件的名字；将方法返回结果放在缓存中，是列表即可以指定多个缓存；
     *  2.key属性：缓存数据使用的key，
     *      2.1 默认是使用方法的参数的值，此处即item
     *      2.2 使用SpEL表达式，例如：#result.id(返回的结果调用id属性的值) 或者 #item.id(入参对象调用id属性的值)
     *  3.keyGenerator属性：key的生成器，可以指定key生成器的组件beanName。
     *      3.1 key/keyGenerator：二选一使用
    * */
    //@CachePut(cacheNames = "items",key = "#items.id")
    @CachePut(key = "#items.id")
    @Override
    public Items updateOne(Items items) {
        int row = baseMapper.updateById(items);
        return items;
    }

    /**
     * 注解 @CacheEvict 清除缓存
     * 属性：
     *  1.cacheNames/value属性：指定缓存组件的名字；将方法返回结果放在缓存中，是列表即可以指定多个缓存；
     *  2.key属性：指定要清除的数据
     *      2.1 默认是使用方法的参数的值，此处为id，即例如：1
     *      2.2 使用SpEL表达式，例如：#id 入参的值
     *  3.allEntries属性：true则指定清除这个缓存中所有的数据
     *  4.beforeInvocation属性：缓存的清除是否在方法之前执行
     *      4.1 false，默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
     *      4.2 true，代表清除缓存操作是在方法运行之前执行，无论方法是否出现异常，缓存都清除
     */
    @CacheEvict(key = "#id",beforeInvocation = true,allEntries = true)
    @Override
    public boolean deleteOne(Integer id) {
        log.info("正在执行删除....id:{}",id);
        int row = baseMapper.deleteById(id);
        //测试异常发生
        //int i = 1/0;
        return row>0;
    }

    @Caching(
            cacheable = @Cacheable(key = "#name"),
            put = {
                    @CachePut(key = "#result.id"),
                    @CachePut(key = "#result.price")
            }
    )
    @Override
    public Items findByQueryName(String name) {
        LambdaQueryWrapper<Items> queryWrapper =
                new LambdaQueryWrapper<Items>().eq(Items::getName, name);
        return baseMapper.selectOne(queryWrapper);
    }
}
